/**
 * 这是一个可以排序的上传组件
 * 在最Upload的基础上使用了React-dnd组件
 *    1：定义了moveRow函数，这个函数用来交换顺序
 *    2：重载了Upload中的itemRender,将原始的originNode传入到自定义的DragableCard。
 *       以便在原始的originNode上包裹一个可以移动的card。
 *
 * 这就是React-dnd设计巧妙的地方，对原始的代码侵入比较小。
 */

import React, { useState, useCallback } from 'react';

// 上传相关
import { Upload, Modal } from 'antd';
import type { UploadChangeParam } from 'antd/lib/upload';
import type { UploadFile } from 'antd/lib/upload/interface';
import { getToken } from '@/services/tokenInfo';
import { PlusOutlined } from '@ant-design/icons';
import { getBase64 } from '@/utils/img';
import { ImgFallback } from '@/services/Common';

// 拖动相关
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';

import DndPictureCard from './DndPictureCard';

/**
 * 组件的入口参数 
   files: 默认的图片文件
[
    {
      uid: '-1',
      name: 'image1.png',
      status: 'done',
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
    {
      uid: '-2',
      name: 'image2.png',
      status: 'done',
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
    {
      uid: '-3',
      name: 'image3.png',
      status: 'done',
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
    {
      uid: '-4',
      name: 'image4.png',
      status: 'done',
      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
    },
  ]
 * 
 */
type PropsType = {
  initFiles: UploadFile<any>[];
  handleDelete?: (fileName: string) => void;
  // 默认是5个
  maxCount: number;
};

/**
 * 向外暴露的方法
 *   getFiles: 返回当前的数据内容
 */
export type refProps = {
  getFiles: () => UploadFile<any>[];
};

/**
 * 可以拖动的图片上传组件
 * @returns
 */
const DndPicturesWall = React.forwardRef<refProps, PropsType>((props: PropsType, ref) => {
  const { initFiles, handleDelete, maxCount } = props;
  const [fileList, setFileList] = useState<UploadFile<any>[]>(initFiles);

  // 定义向外暴漏的方法
  React.useImperativeHandle(ref, () => ({
    getFiles: () => {
      return fileList;
    },
  }));

  /**
   * 这个函数用来交换顺序
   * 会被作为属性传递DragableCard组件中
   * useCallback使用说明：
   *     useCallback的用法与useState的用法基本一致，但最后会返回一个函数，用一个变量保存起来。
   *     只有fileList变化了，这个函数才变化，避免函数在不必要的情况下更新。
   *     这里使用了immutability-helper的update方法，来交换数组。
   */
  const moveRow = useCallback(
    (dragIndex, hoverIndex) => {
      const dragRow = fileList[dragIndex];
      setFileList(
        update(fileList, {
          $splice: [
            [dragIndex, 1],
            [hoverIndex, 0, dragRow],
          ],
        }),
      );
    },
    [fileList],
  );

  // 为了弹出图片预览，所需要的数据结构
  const [previewInfo, setPreviewInfo] = React.useState({
    title: '',
    src: '',
  });

  const [previewVisible, setPreviewVisible] = React.useState(false);

  /**
   * 图片预览
   * @param file 图片的函数
   */
  const onPreview = async (file: UploadFile) => {
    let preview;
    if (!file.url && !file.preview && file.originFileObj) {
      preview = await getBase64(file.originFileObj);
    }
    setPreviewVisible(true);
    const src = file.url || preview;

    if (src) {
      setPreviewInfo({
        // title: file.name || file.url.substring(file.url.lastIndexOf('/') + 1),
        title: '图片预览',
        // @ts-ignore
        src,
      });
    } else {
      // 显示一个默认的数值，如果图片找不到了
      setPreviewInfo({
        title: '图片未找到',
        src: ImgFallback,
      });
    }
  };

  /**
   * 点击移除文件时的回调，返回值为 false 时不移除。支持返回一个 Promise 对象，Promise 对象 resolve(false) 或 reject 时不移除
   * @param file
   */
  const onRemove = (file: UploadFile) => {
    if (handleDelete) {
      handleDelete(file.url || 'noUpload');
    }
    return true;
  };

  /**
   * 图片列表中的内容发生变化时，触发这个事件
   * @param info
   */
  const onChange = (info: UploadChangeParam) => {
    if (info.file.status === 'done') {
      // console.log(info.file.response);
    }
    // 将最终的图片信息info.fileList，设置到fileList中。
    setFileList(info.fileList);
  };

  // 上传的图片控件
  const UploadButton = (
    <div>
      <PlusOutlined />
      <div style={{ marginTop: 8 }}>Upload</div>
    </div>
  );

  return (
    <>
      <DndProvider backend={HTML5Backend}>
        <Upload
          action="/api/admin/upload/temp"
          listType="picture-card"
          headers={{ Authorization: getToken() as string }}
          fileList={fileList}
          onPreview={onPreview}
          onRemove={onRemove}
          onChange={onChange}
          itemRender={(originNode, file, currFileList) => (
            <DndPictureCard
              originNode={originNode}
              file={file}
              fileList={currFileList}
              moveRow={moveRow}
            />
          )}
        >
          {fileList.length >= maxCount ? null : UploadButton}
        </Upload>
      </DndProvider>

      <Modal
        visible={previewVisible}
        title={previewInfo.title}
        footer={null}
        onCancel={() => setPreviewVisible(false)}
      >
        <img alt="example" style={{ width: '100%' }} src={previewInfo.src} />
      </Modal>
    </>
  );
});

DndPicturesWall.defaultProps = {
  maxCount: 5,
};

export default DndPicturesWall;
